﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls.Primitives;

namespace Gmantis.Controls.DataGrid
{
    internal class DataGridScrollManager
    {
        // Fields
        private Size _availableSize;
        private FillParameters _fillParameters;
        private double _horizontalOffset;
        private double _verticalOffset;

        // Events
        public event EventHandler AvailableSizeChanged;

        public event EventHandler HorizontalOffsetChanged;

        public event EventHandler HorizontalScrollChanged;

        public event EventHandler VerticalOffsetChanged;

        public event EventHandler VerticalScrollChanged;

        // Methods
        public DataGridScrollManager(DataGridView view)
        {
            EventHandler handler = null;
            EventHandler handler2 = null;
            this._availableSize = new Size();
            FillParameters parameters = new FillParameters
            {
                RowIndex = -1,
                ColumnIndex = -1
            };
            this._fillParameters = parameters;
            this.View = view;
            this.HorizontalScrollStrategy = new DataGridColumnsVariableScrollStrategy(this.View);
            this.VerticalScrollStrategy = new DataGridRowsVariableScrollStrategy(this.View);
            if (handler == null)
            {
                handler = delegate(object s, EventArgs e)
                {
                    if (this.HorizontalScrollChanged != null)
                    {
                        this.HorizontalScrollChanged(this, new EventArgs());
                    }
                };
            }
            this.HorizontalScrollStrategy.Changed += handler;
            if (handler2 == null)
            {
                handler2 = delegate(object s, EventArgs e)
                {
                    if (this.VerticalScrollChanged != null)
                    {
                        this.VerticalScrollChanged(this, new EventArgs());
                    }
                };
            }
            this.VerticalScrollStrategy.Changed += handler2;
        }

        public int GetColumnDisplayIndexFromScrollPosition(double scrollPosition, out double offset)
        {
            return this.HorizontalScrollStrategy.GetItemIndexFromScrollPosition(scrollPosition, out offset);
        }

        public void GetCurrentHorizontalBounds(double viewportWidth, out int firstColumn, out int lastColumn)
        {
            double num2;
            double num = Math.Max((double)0.0, (double)(viewportWidth - this.View.GetMinFrozenWidth()));
            FillParameters validFillParameters = this.ValidFillParameters;
            if (validFillParameters.HorizontalDirection == GeneratorDirection.Forward)
            {
                double scrollPositionFromColumnDisplayIndex = this.GetScrollPositionFromColumnDisplayIndex(validFillParameters.ColumnIndex);
                double num3 = scrollPositionFromColumnDisplayIndex - validFillParameters.ColumnOffset;
                firstColumn = validFillParameters.ColumnIndex;
                lastColumn = this.GetColumnDisplayIndexFromScrollPosition(num3 + num, out num2);
            }
            else
            {
                double introduced6 = this.GetScrollPositionFromColumnDisplayIndex(validFillParameters.ColumnIndex + 1);
                double num4 = introduced6 + validFillParameters.ColumnOffset;
                lastColumn = validFillParameters.ColumnIndex;
                firstColumn = this.GetColumnDisplayIndexFromScrollPosition(num4 - num, out num2);
            }
        }

        public void GetCurrentVerticalBounds(double viewportHeight, out int firstRow, out int lastRow)
        {
            double num2;
            double num = Math.Max((double)0.0, (double)(viewportHeight - this.View.GetFrozenHeight()));
            if (this.ValidFillParameters.VerticalDirection == GeneratorDirection.Forward)
            {
                double num3 = this.GetScrollPositionFromRowIndex(this.ValidFillParameters.RowIndex) - this.ValidFillParameters.RowOffset;
                firstRow = this.ValidFillParameters.RowIndex;
                lastRow = this.GetRowIndexFromScrollPosition(num3 + num, out num2);
            }
            else
            {
                double num4 = this.GetScrollPositionFromRowIndex(this.ValidFillParameters.RowIndex + 1) + this.ValidFillParameters.RowOffset;
                lastRow = this.ValidFillParameters.RowIndex;
                firstRow = this.GetRowIndexFromScrollPosition(num4 - num, out num2);
            }
        }

        public RelativePosition GetRelativePositionHorizontally(double relativeScreenX, double? horizontalOffset = new double?())
        {
            int index = -1;
            double num2 = (this.FrozenLeftWidth + this.View.Model.VerticalFreezingSeparatorWidth) + this.View.GetRenderLeadingColumnWidth();
            if (relativeScreenX < 0.0)
            {
                return new RelativePosition(RelativePositionType.BeforeFirstFrozen);
            }
            if (relativeScreenX >= Math.Min(this.Viewport.Width, (this.ExtentWidth + num2) + this.FrozenRightWidth))
            {
                return new RelativePosition(RelativePositionType.AfterLastFrozen);
            }
            if (relativeScreenX >= num2)
            {
                double num5;
                double? nullable = horizontalOffset;
                index = this.GetColumnDisplayIndexFromScrollPosition((nullable.HasValue ? nullable.GetValueOrDefault() : this.HorizontalOffset) + (relativeScreenX - num2), out num5);
                return new RelativePosition(RelativePositionType.ScrollingArea, index);
            }
            double num3 = 0.0;
            for (int i = 0; i < this.View.Model.FrozenColumnCount; i++)
            {
                DataGridColumn column = this.View.Model.Columns.OrderedColumns[i];
                if (relativeScreenX < (num3 + column.ViewColumn.RenderWidth))
                {
                    index = i;
                    break;
                }
                num3 += column.ViewColumn.RenderWidth;
            }
            return new RelativePosition(RelativePositionType.FirstFrozen, index);
        }

        public RelativePosition GetRelativePositionVertically(double relativeScreenY, double? verticalOffset = new double?())
        {
            int rowIndexFromScrollPosition = -1;
            if (relativeScreenY < 0.0)
            {
                return new RelativePosition(RelativePositionType.BeforeFirstFrozen);
            }
            if (relativeScreenY >= this.FrozenTopHeight)
            {
                if (relativeScreenY < (this.FrozenTopHeight + Math.Min(this.ScrollingHeight, this.ExtentHeight)))
                {
                    double num4;
                    double? nullable = verticalOffset;
                    rowIndexFromScrollPosition = this.GetRowIndexFromScrollPosition((nullable.HasValue ? nullable.GetValueOrDefault() : this.VerticalOffset) + (relativeScreenY - this.FrozenTopHeight), out num4);
                    DataGridRow row2 = this.View.Model.Rows[Math.Max(0, Math.Min(this.View.Model.Rows.Count - 1, rowIndexFromScrollPosition))];
                    return new RelativePosition(RelativePositionType.ScrollingArea, rowIndexFromScrollPosition, (row2.ViewRow == null) || (-num4 < row2.ViewRow.RenderHeight));
                }
                if ((this.View.Model.FrozenBottomRowsCount > 0) && (relativeScreenY < (this.Viewport.Height - this.FrozenBottomHeight)))
                {
                    return new RelativePosition(RelativePositionType.EmptyZone);
                }
                if ((this.View.Model.FrozenBottomRowsCount <= 0) || (relativeScreenY >= this.Viewport.Height))
                {
                    return new RelativePosition(RelativePositionType.AfterLastFrozen);
                }
                double num5 = 0.0;
                rowIndexFromScrollPosition = this.View.Model.Rows.Count - 1;
                for (int j = this.View.Model.Rows.Count - this.View.Model.FrozenBottomRowsCount; j < this.View.Model.Rows.Count; j++)
                {
                    DataGridRow row3 = this.View.Model.Rows[j];
                    if ((relativeScreenY - (this.Viewport.Height - this.FrozenBottomHeight)) < (num5 + row3.ViewRow.RenderHeight))
                    {
                        rowIndexFromScrollPosition = j;
                        break;
                    }
                    num5 += row3.ViewRow.RenderHeight;
                }
                return new RelativePosition(RelativePositionType.LastFrozen, rowIndexFromScrollPosition);
            }
            double num2 = 0.0;
            rowIndexFromScrollPosition = 0;
            for (int i = 0; i < this.View.Model.FrozenTopRowsCount; i++)
            {
                DataGridRow row = this.View.Model.Rows[i];
                if (relativeScreenY < (num2 + row.ViewRow.RenderHeight))
                {
                    rowIndexFromScrollPosition = i;
                    break;
                }
                num2 += row.ViewRow.RenderHeight;
            }
            return new RelativePosition(RelativePositionType.FirstFrozen, rowIndexFromScrollPosition);
        }

        public int GetRowIndexFromScrollPosition(double scrollPosition, out double offset)
        {
            return this.VerticalScrollStrategy.GetItemIndexFromScrollPosition(scrollPosition, out offset);
        }

        public double GetScrollPositionFromColumnDisplayIndex(int columnDisplayIndex)
        {
            return this.HorizontalScrollStrategy.GetScrollPositionFromItemIndex(columnDisplayIndex);
        }

        public double GetScrollPositionFromRowIndex(int rowIndex)
        {
            if (rowIndex < this.View.Model.Rows.Count)
            {
                return this.VerticalScrollStrategy.GetScrollPositionFromItemIndex(rowIndex);
            }
            return this.VerticalScrollStrategy.ExtentLength;
        }

        public DataGridRow PageDown()
        {
            if (!double.IsInfinity(this.AvailableSize.Height) && (this.View.Model.Rows.Count > 0))
            {
                double num;
                int itemIndexFromScrollPosition = this.VerticalScrollStrategy.GetItemIndexFromScrollPosition(Math.Min(this.ExtentHeight, this.VerticalOffset + (2.0 * this.ScrollingHeight)), out num);
                itemIndexFromScrollPosition = Math.Max(0, Math.Min(itemIndexFromScrollPosition, this.View.Model.Rows.Count - 1));
                this.ScrollVertically(itemIndexFromScrollPosition, 0.0, GeneratorDirection.Backward);
                return this.View.Model.Rows[itemIndexFromScrollPosition];
            }
            return null;
        }

        public DataGridRow PageUp()
        {
            if (!double.IsInfinity(this.AvailableSize.Height) && (this.View.Model.Rows.Count > 0))
            {
                double num;
                int itemIndexFromScrollPosition = this.VerticalScrollStrategy.GetItemIndexFromScrollPosition(Math.Max((double)0.0, (double)(this.VerticalOffset - this.ScrollingHeight)), out num);
                itemIndexFromScrollPosition = Math.Max(0, Math.Min(itemIndexFromScrollPosition, this.View.Model.Rows.Count - 1));
                this.ScrollVertically(itemIndexFromScrollPosition, 0.0, GeneratorDirection.Forward);
                return this.View.Model.Rows[itemIndexFromScrollPosition];
            }
            return null;
        }

        public void ResetScroll()
        {
            this.ScrollHorizontally(-1, 0.0, GeneratorDirection.Forward);
            this.ScrollVertically(-1, 0.0, GeneratorDirection.Forward);
        }

        internal void ScrollHorizontally(double horizontalOffset)
        {
            double num;
            int itemIndexFromScrollPosition = this.HorizontalScrollStrategy.GetItemIndexFromScrollPosition(horizontalOffset, out num);
            this.ScrollHorizontally(itemIndexFromScrollPosition, num, GeneratorDirection.Forward);
        }

        internal void ScrollHorizontally(int columnDisplayIndex, double columnOffset, GeneratorDirection direction)
        {
            this.ScrollHorizontally(columnDisplayIndex, columnOffset, direction, false);
        }

        internal void ScrollHorizontally(int columnDisplayIndex, double columnOffset, GeneratorDirection direction, bool avoidInvalidating)
        {
            try
            {
                this._fillParameters.ColumnIndex = columnDisplayIndex;
                this._fillParameters.ColumnOffset = columnOffset;
                this._fillParameters.HorizontalDirection = direction;
                if (!avoidInvalidating)
                {
                    this.View.RowsPanel.InvalidateMeasure();
                    this.View.RowsPanel.InvalidateRowsMeasure();
                    if (this.View.ColumnsHeaderPanel != null)
                    {
                        this.View.ColumnsHeaderPanel.InvalidateMeasure();
                    }
                }
            }
            catch (DataGridFinalizeEditingException)
            {
            }
        }

        public void ScrollIntoViewHorizontally(int columnDisplayIndex)
        {
            int num;
            int num2;
            this.GetCurrentHorizontalBounds(this.Viewport.Width, out num, out num2);
            if ((num < 0) || (num2 < 0))
            {
                this.ScrollHorizontally(0, 0.0, GeneratorDirection.Forward);
            }
            else if (columnDisplayIndex <= num)
            {
                this.ScrollHorizontally(columnDisplayIndex, 0.0, GeneratorDirection.Forward);
            }
            else if (columnDisplayIndex >= num2)
            {
                this.ScrollHorizontally(columnDisplayIndex, 0.0, GeneratorDirection.Backward);
            }
        }

        public void ScrollIntoViewVertically(int rowIndex)
        {
            int num;
            int num2;
            this.GetCurrentVerticalBounds(this.Viewport.Height, out num, out num2);
            if ((num < 0) || (num2 < 0))
            {
                this.ScrollVertically(0, 0.0, GeneratorDirection.Forward);
            }
            else if (rowIndex <= num)
            {
                this.ScrollVertically(rowIndex, 0.0, GeneratorDirection.Forward);
            }
            else if (rowIndex >= num2)
            {
                this.ScrollVertically(rowIndex, 0.0, GeneratorDirection.Backward);
            }
        }

        public void ScrollSteps(int stepsCount)
        {
            double num;
            if (!this.View.DataGrid.VerticalScrollStep.IsDiscrete)
            {
                if (this.View.DataGrid.VerticalScrollStep.IsAbsolute)
                {
                    this.ScrollVertically(this.VerticalOffset + (stepsCount * this.View.DataGrid.VerticalScrollStep.Value));
                }
                return;
            }
            int itemIndexFromScrollPosition = this.VerticalScrollStrategy.GetItemIndexFromScrollPosition(this.VerticalOffset, out num);
            IEnumerator<DataGridRow> enumerator = this.View.Model.Rows.GetVisibleRowsFromIndex(itemIndexFromScrollPosition, (stepsCount > 0) ? GeneratorDirection.Forward : GeneratorDirection.Backward, false).GetEnumerator();
            DataGridRow current = null;
            int num3 = 0;
        Label_0073: ;
            if ((num3 <= (Math.Abs(stepsCount) + (((num < 0.0) && (stepsCount < 0)) ? -1 : 0))) && enumerator.MoveNext())
            {
                current = enumerator.Current;
                num3++;
                goto Label_0073;
            }
            if (current != null)
            {
                this.ScrollVertically(current.Index, 0.0, GeneratorDirection.Forward);
            }
        }

        internal void ScrollVertically(double verticalOffset)
        {
            double num;
            int itemIndexFromScrollPosition = this.VerticalScrollStrategy.GetItemIndexFromScrollPosition(verticalOffset, out num);
            this.ScrollVertically(itemIndexFromScrollPosition, num, GeneratorDirection.Forward);
        }

        internal void ScrollVertically(int rowDisplayIndex, double rowOffset, GeneratorDirection direction)
        {
            try
            {
                this._fillParameters.RowIndex = rowDisplayIndex;
                this._fillParameters.RowOffset = rowOffset;
                this._fillParameters.VerticalDirection = direction;
                this.View.RowsPanel.InvalidateMeasure();
            }
            catch (DataGridFinalizeEditingException)
            {
            }
        }

        // Properties
        public double AvailableCellsAreaWidth
        {
            get
            {
                return Math.Max(0.0, double.IsInfinity(this.AvailableSize.Width) ? double.PositiveInfinity : (this.AvailableSize.Width - this.View.GetRenderLeadingColumnWidth()));
            }
        }

        public Size AvailableSize
        {
            get
            {
                return this._availableSize;
            }
            set
            {
                if (this._availableSize != value)
                {
                    this._availableSize = value;
                    if (this.AvailableSizeChanged != null)
                    {
                        this.AvailableSizeChanged(this, new EventArgs());
                    }
                }
            }
        }

        public double CellsAreaWidth
        {
            get
            {
                return Math.Max((double)0.0, (double)(this.Viewport.Width - this.View.GetRenderLeadingColumnWidth()));
            }
        }

        public double ExtentHeight
        {
            get
            {
                return this.VerticalScrollStrategy.ExtentLength;
            }
        }

        public double ExtentWidth
        {
            get
            {
                return this.HorizontalScrollStrategy.ExtentLength;
            }
        }

        public double FrozenBottomHeight { get; internal set; }

        public double FrozenLeftWidth { get; internal set; }

        public double FrozenRightWidth { get; internal set; }

        public double FrozenTopHeight { get; internal set; }

        public double HorizontalOffset
        {
            get
            {
                return this._horizontalOffset;
            }
            internal set
            {
                if (this._horizontalOffset != value)
                {
                    this._horizontalOffset = value;
                    if (this.HorizontalOffsetChanged != null)
                    {
                        this.HorizontalOffsetChanged(this, new EventArgs());
                    }
                }
            }
        }

        private IScrollStrategy HorizontalScrollStrategy { get; set; }

        public double ScrollingHeight
        {
            get
            {
                return Math.Max((double)0.0, (double)(this.Viewport.Height - this.View.GetFrozenHeight()));
            }
        }

        public double ScrollingWidth
        {
            get
            {
                return Math.Max((double)0.0, (double)(this.CellsAreaWidth - this.View.GetFrozenWidth(false)));
            }
        }

        internal FillParameters ValidFillParameters
        {
            get
            {
                int num = Math.Max(this.View.Model.FrozenTopRowsCount, Math.Min((this.View.Model.Rows.Count - 1) - this.View.Model.FrozenBottomRowsCount, this._fillParameters.RowIndex));
                int num2 = Math.Max(this.View.Model.FrozenColumnCount, Math.Min(this.View.Model.Columns.OrderedColumns.Length - 1, this._fillParameters.ColumnIndex));
                return new FillParameters { RowIndex = num, RowOffset = (num == this._fillParameters.RowIndex) ? this._fillParameters.RowOffset : 0.0, VerticalDirection = this._fillParameters.VerticalDirection, ColumnIndex = num2, ColumnOffset = (num2 == this._fillParameters.ColumnIndex) ? this._fillParameters.ColumnOffset : 0.0, HorizontalDirection = this._fillParameters.HorizontalDirection };
            }
        }

        public double VerticalOffset
        {
            get
            {
                return this._verticalOffset;
            }
            internal set
            {
                if (this._verticalOffset != value)
                {
                    this._verticalOffset = value;
                    if (this.VerticalOffsetChanged != null)
                    {
                        this.VerticalOffsetChanged(this, new EventArgs());
                    }
                }
            }
        }

        private IScrollStrategy VerticalScrollStrategy { get; set; }

        public DataGridView View { get; private set; }

        public Size Viewport { get; set; }
    }
}
